package tasks;

import static org.reflections.ReflectionUtils.getAllMethods;
import static org.reflections.ReflectionUtils.withPrefix;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;

import util.StringUtil;

public class GenerateModel {

	static private int indent = 0;
	private static PrintWriter pw;

	public static void main(String[] args) throws SecurityException,
			NoSuchFieldException, IOException {

		Reflections r = new Reflections("models", new SubTypesScanner(false));
		File f = new File("jscala/jsmodels/models.scala");
		if (f.exists())
			f.delete();
		f.createNewFile();
		// pw = new PrintWriter(System.out);

		pw = new PrintWriter(f);
		print("package jsmodels");
		print("import traits.ArrayJs");

		Set<Class<?>> referidos = new java.util.HashSet<Class<?>>();
		// getClassesInPackage("models")
		List<Class<?>> classes = new ArrayList<Class<?>>(
				r.getSubTypesOf(Object.class));
		Collections.sort(classes, new Comparator<Class<?>>() {

			@Override
			public int compare(Class<?> o1, Class<?> o2) {
				return o1.getSimpleName().compareTo(o2.getSimpleName());
			}
		});

		for (Class<? extends Object> c : classes) {
			if (c.getSimpleName().startsWith("package"))
				continue;
			if (c.isInterface() || Modifier.isAbstract(c.getModifiers()))
				continue;
			writeTrait(c, referidos);
		}
		for (Class<?> c : referidos)
			writeEnum(c);
		pw.flush();
		pw.close();
	}

	private static void writeEnum(Class<?> c) {
		print("object " + c.getSimpleName() + "{ // " + c.getCanonicalName());
		indent();
		for (Field f : c.getFields())
			print("val " + f.getName() + " = \"" + f.getName() + "\"");

		deindent();
		print("}");
	}

	@SuppressWarnings("unchecked")
	private static void writeTrait(Class<? extends Object> c,
			Set<Class<?>> referidos) throws SecurityException,
			NoSuchFieldException {
		print("trait " + c.getSimpleName() + "{ // " + c.getCanonicalName());
		indent();
		for (Method m : getAllMethods(c, withPrefix("get"))) {
			if (m.getName().equals("get"))
				continue;
			if (m.getDeclaringClass() != c)
				continue;
			if (Modifier.isVolatile(m.getModifiers()))
				continue;
			String r = null;
			Class<?> returnType = m.getReturnType();
			if (returnType.isEnum())
				r = "String";
			else if (List.class.isAssignableFrom(returnType))
				r = "ArrayJs[Any]";
			else if (Date.class.isAssignableFrom(returnType))
				r = "Long";
			else if (returnType.getSimpleName().equals("long"))
				r = "Long";
			else
				r = returnType.getSimpleName();
			print("var "
					+ StringUtil.firstToLower(m.getName().replace("get", ""))
					+ ":" + r + " // " + Modifier.toString(m.getModifiers())
					+ " " + m.getDeclaringClass().getSimpleName() + "."
					+ m.getName());
			if (returnType.isEnum())
				referidos.add(returnType);
		}

		deindent();
		print("}");
	}

	public static void print(Object o) {
		String s = "";
		for (int i = 0; i < indent; i++)
			s += "\t";

		System.out.println(s + o);
		pw.println(s + o);

	}

	public static void indent() {
		indent++;
	}

	public static void deindent() {
		indent--;
	}
}
